package com.kunpan.admin.controller;

import cn.dev33.satoken.annotation.SaCheckLogin;
import cn.dev33.satoken.stp.StpUtil;
import com.kunpan.admin.annotation.AutoFill;
import com.kunpan.admin.async.AsyncService;
import com.kunpan.admin.async.EmailService;
import com.kunpan.admin.common.Enum.BusinessType;
import com.kunpan.admin.common.Enum.EmailEnum;
import com.kunpan.admin.common.domain.AjaxResult;
import com.kunpan.admin.common.redis.RedisCache;
import com.kunpan.admin.common.utils.CaptchaUtils;
import com.kunpan.admin.common.utils.RefreshTokenUtils;
import com.kunpan.admin.common.utils.SecurityUtils;
import com.kunpan.admin.common.utils.StringUtils;
import com.kunpan.admin.constant.Constants;
import com.kunpan.admin.constant.HttpStatus;
import com.kunpan.admin.entity.LoginBody;
import com.kunpan.admin.entity.RegisterBody;
import com.kunpan.admin.entity.User;
import com.kunpan.admin.exception.file.InsufficientSpaceException;
import com.kunpan.admin.service.UserService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.repository.query.Param;
import org.springframework.util.DigestUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Base64;
import java.util.Map;
import java.util.Objects;

@RestController
@RequestMapping("/user")
public class UserController {

    private final Logger log = LoggerFactory.getLogger(AsyncService.class);
    @Autowired
    private UserService userService;

    @Autowired
    private RedisCache redisCache;

    @Autowired
    private EmailService emailService;


    /**
     * 注册
     */
    @AutoFill(value = BusinessType.INSERT)
    @PostMapping("/register")
    public AjaxResult register(@RequestBody RegisterBody registerBody) {
        String codeKey = StringUtils.getEmailKey(registerBody.getEmail(), EmailEnum.REGISTER.getMessage());
        if (redisCache.hasKey(codeKey)) {
            //根据邮箱拿到值
            String emailCode = redisCache.getCacheObject(codeKey);
            if (!emailCode.equals(registerBody.getEmailCode())) {
                return AjaxResult.error("验证码错误");
            }
        } else {
            return AjaxResult.error("验证码已过期");
        }
        String msg = "";
        msg = userService.register(registerBody);
        return StringUtils.isEmpty(msg) ? AjaxResult.success("注册成功") : AjaxResult.error(msg);
    }

    /**
     * 登入
     */
    @AutoFill(value = BusinessType.RECORD)
    @PostMapping("/login")
    public AjaxResult login(@RequestBody LoginBody loginBody, HttpServletRequest request) {
        //uuid是在生成验证码同时生成的随机字符串，用于从redis拿到生成的正确验证码，code是用户输入的验证码
        if (!redisCache.hasKey(Constants.CAPTCHA_KEY + loginBody.getUuid())) {//判断此验证码是否存在redis缓存中
            return AjaxResult.warn("验证码已过期");
        } else {
            String redisCode = redisCache.getCacheObject(Constants.CAPTCHA_KEY + loginBody.getUuid());
            if (!redisCode.equals(loginBody.getCode())) {
                return AjaxResult.error("验证码输入错误");
            }
        }
        return userService.login(loginBody.getEmail(), loginBody.getPassword(), request);
    }

    /**
     * 退出登入
     */
    @SaCheckLogin
    @PostMapping("/logout")
    public AjaxResult logout(HttpServletRequest request) {
        return userService.logout(request);
    }

    /**
     * 图形验证码
     */
    @GetMapping("/captcha")
    public AjaxResult getImgCaptcha(HttpServletRequest request, HttpServletResponse response) {
        byte[] img;
        Map<String, Object> map;
        try {
            map = CaptchaUtils.generateCaptchaImage(redisCache);
            img = (byte[]) map.get("imageBytes");
            String base64Image = Base64.getEncoder().encodeToString(img);
            map.remove("imageBytes");
            map.put("img", base64Image);
            response.setContentType("application/json");
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        return AjaxResult.success(map);
    }

    /**
     * 邮件验证码发送
     */
    @GetMapping("/sendEmailMessage")
    public AjaxResult sendSimpleMessage(String email, Integer type) {//type 0表示注册，1找回密码
        try {
            String title = "";
            Integer userId = userService.selectUserIdByEmail(email);
            if (Objects.equals(EmailEnum.REGISTER.getCode(), type)) {
                if (userId != null) {
                    return AjaxResult.error("此用户已存在");
                }
                title = EmailEnum.REGISTER.getMessage();
            } else {
                if (userId == null) {
                    return AjaxResult.error("此邮箱未注册");
                }
                title = EmailEnum.RECOVER_PASSWORD.getMessage();
            }
            String codeKey = StringUtils.getEmailKey(email, title);
            String countKey = StringUtils.getEmailKey(email, "");
            //判断键是否存在，判断2分钟之内是不是第一次发送找回密码的验证码
            if (redisCache.hasKey(countKey)) {
                if (redisCache.<Integer>getCacheObject(countKey) >= 10) {
                    return AjaxResult.error("验证码发送次数过多，请稍后重试");
                }
            }
            emailService.sendVerificationCode(email, codeKey, countKey, title);
            return AjaxResult.success("已发送,请注意查收");
        } catch (Exception e) {
            return AjaxResult.error("发送失败，请检查网络是否连接");
        }
    }

    /**
     * 获取用户信息
     */
    @SaCheckLogin
    @GetMapping("/profile")
    public AjaxResult getUserInfo() {
        Integer userId = Integer.valueOf(StpUtil.getLoginId().toString());
        return userService.selectUserById(userId);
    }

    /**
     * 更新用户信息
     */
    @AutoFill(BusinessType.UPDATE)
    @SaCheckLogin
    @PutMapping("/profile/updateInfo")
    public AjaxResult updateUserInf(@RequestBody User user) {
        return userService.updateUserInfo(user);
    }

    /**
     * 修改密码
     */
    @SaCheckLogin
    @PutMapping("/profile/updatePwd")
    public AjaxResult updatePwd(String oldPassword, String newPassword) {
        User user = (User) StpUtil.getSession().get(Constants.LOGIN_USER);
        Integer userId = Integer.valueOf(StpUtil.getLoginId().toString());
        String password = user.getPassword();
        if (!SecurityUtils.matchesPassword(oldPassword, password)) {
            return AjaxResult.error("修改密码失败，旧密码错误");
        }
        if (SecurityUtils.matchesPassword(newPassword, password)) {
            return AjaxResult.error("新密码不能与旧密码相同");
        }
        newPassword = SecurityUtils.encryptPassword(newPassword);
        if (userService.resetUserPwd(userId, newPassword) > 0) {
            // 更新缓存用户密码
            ((User) StpUtil.getSession().get(Constants.LOGIN_USER)).setPassword(newPassword);
            user.setPassword(newPassword);
            user.setNickname("9999");
            return AjaxResult.success();
        }
        return AjaxResult.error("修改密码异常，请联系管理员");
    }

    /**
     * 修改头像
     */
    @PostMapping("/avatar")
    public AjaxResult updateAvatar(@Param("avatarFile") MultipartFile avatarFile, @Param("fileMd5") String fileMd5) {
        return userService.updateAvatar(avatarFile, fileMd5);
    }

    /**
     * token过期，刷新token
     */
    @PostMapping("/refresh")
    public AjaxResult refresh(@RequestBody Map<String, String> tokenBody, HttpServletRequest request) {
        return userService.refreshToken(tokenBody, request);
    }

    /**
     * 查询当前用户剩余空间
     */
    @GetMapping("/space")
    public AjaxResult getFreeSpace() {
        return userService.getFreeSpace();
    }

    /**
     * 忘记密码，进行重置
     */
    @PutMapping("/forgetPwd")
    public AjaxResult forgetPassword(@RequestBody RegisterBody registerBody) {
        String codeKey = StringUtils.getEmailKey(registerBody.getEmail(), EmailEnum.RECOVER_PASSWORD.getMessage());
        String email = registerBody.getEmail();
        String password = registerBody.getPassword();
        //根据邮箱拿到值
        String emailCode = redisCache.getCacheObject(codeKey);
        if(emailCode == null){
            return AjaxResult.error("验证码已过期");
        }
        if (!emailCode.equals(registerBody.getEmailCode())) {
            return AjaxResult.error("验证码输入错误");
        }
        return userService.forgetPassword(email, password);
    }
}
